﻿using System.Diagnostics;
using System.Text;

namespace DimensionsHelper.LanguageService.Serialization;

/// <summary>
///     IBM DataCollection Script 脚本生成器
/// </summary>
public class CodeGenerator
{
    private readonly Stack<string> _contextStack = new();
    private readonly SourceTextWriter _writer = new();


    public void WriteLine(string text)
    {
        _writer.AppendLine(text);
    }


    /// <summary>
    ///     追加一个单行注释
    /// </summary>
    /// <param name="text"></param>
    public void WriteSingleLineComment(string text)
    {
        _writer.AppendLine("' " + text);
    }


    /// <summary>
    ///     追加一个多行注释
    /// </summary>
    /// <param name="lines"></param>
    public void WriteMultiLineComment(params string[] lines)
    {
        _writer.AppendLine("'!");
        foreach (string line in lines)
        {
            _writer.AppendLine(" ! " + line);
        }

        _writer.AppendLine(" !'");
    }


    public void WriteIdentifier(string name)
    {
        _writer.Append(name);
    }


    /// <summary>
    ///     声明一个变量
    /// </summary>
    public void DeclareVariable(string name)
    {
        _writer.AppendLine("Dim " + name);
    }


    /// <summary>
    ///     声明多个变量
    /// </summary>
    public void DeclareVariables(params string[] names)
    {
        _writer.Append("Dim ");

        for (int i = 0; i < names.Length; i++)
        {
            if (i > 0)
            {
                _writer.Append(", ");
            }

            _writer.Append(names[i]);
        }

        _writer.AppendLine();
    }


    /// <summary>
    ///     声明一个数组
    /// </summary>
    public void DeclareArray(string name, params int?[] boundaries)
    {
        _writer.Append("Dim ");
        _writer.Append(name);

        foreach (int? bound in boundaries)
        {
            _writer.Append('[');
            if (bound != null)
            {
                _writer.Append(bound.ToString()!);
            }

            _writer.Append(']');
        }

        _writer.AppendLine();
    }


    /// <summary>
    ///     添加一个 #include "file_name" 预处理语句
    /// </summary>
    public void IncludeFile(string fileName)
    {
        _writer.Append("#include ");
        _writer.AppendLine("\"" + fileName + "\"");
    }


    public void WriteCreateObject(string left, string typeName)
    {
        _writer.AppendLine($"Set {left} = CreateObject(\"{typeName}\")");
    }


    public void WriteCallExpression(string callee, string?[] args)
    {
        _writer.AppendLine($"{callee}({string.Join(", ", args)})");
    }


    public void WriteCallExpression(string[] calleeMembers, string?[] args)
    {
        WriteCallExpression(string.Join('.', calleeMembers), args);
    }


    public void WriteAssignment(bool setKeyword, string left, string right)
    {
        _writer.AppendLine($"{(setKeyword ? "Set " : "")}{left} = {right}");
    }


    public void WriteAssignment(bool setKeyword, string[] left, string right)
    {
        _writer.AppendLine($"{(setKeyword ? "Set " : "")}{string.Join('.', left)} = {right}");
    }


    public void WriteAssignment(bool setKeyword, string[] left, string[] callee, params string?[] args)
    {
        if (setKeyword)
        {
            _writer.Append("Set ");
        }

        _writer.Append(string.Join('.', left));
        _writer.Append(" = ");
        WriteCallExpression(callee, args);
    }


    public void EndContext()
    {
        Debug.Assert(_contextStack.Count > 0);
        string last = _contextStack.Pop();
        _writer.IndentLevel--;
        _writer.AppendLine(last);
    }


    public void StartWithContext(string expression)
    {
        _writer.AppendLine($"With {expression}");
        _contextStack.Push("End With");
        _writer.IndentLevel++;
    }


    public void StartWithContext(string[] members)
    {
        _writer.AppendLine($"With {string.Join('.', members)}");
        _contextStack.Push("End With");
        _writer.IndentLevel++;
    }


    public void StartWithContext(string[] callee, string?[] args)
    {
        _writer.AppendLine($"With {string.Join('.', callee)}({string.Join(", ", args)})");
        _contextStack.Push("End With");
        _writer.IndentLevel++;
    }


    public void StartFunctionDeclarationContext(FunctionKind kind, string name, params string[] parameters)
    {
        _contextStack.Push("End Function");
    }


    public void WriteExpression(string expression)
    {
        _writer.AppendLine(expression);
    }


    public void StartIfBlock(string expression)
    {
        _writer.AppendLine($"If {expression}");
        _writer.IndentLevel++;
        _contextStack.Push("End If");
    }


    public void StartForContext(string counter, string lower, string heigher, int? step)
    {
        _writer.AppendLine($"For {counter} = {lower} To {heigher}{(step == null ? "" : $" Step {step}")}");
        _contextStack.Push("Next");
        _writer.IndentLevel++;
    }


    public void StartForEachContext(string element, string group)
    {
        _writer.AppendLine($"For Each {element} In {group}");
        _contextStack.Push("Next");
        _writer.IndentLevel++;
    }


    public void StartDoContext(string testExpression, bool loopUntil = false)
    {
        _writer.AppendLine("Do");
        _contextStack.Push($"Loop {(loopUntil ? "Until" : "While")} {testExpression}");
        _writer.IndentLevel++;
    }


    public void StartWhileContext(string testExpression)
    {
        _writer.AppendLine($"While {testExpression}");
        _contextStack.Push("EndWhile");
        _writer.IndentLevel++;
    }


    /// <summary>
    ///     生成当前语法配置的文件。
    /// </summary>
    /// <param name="fileStream">目标文件流</param>
    /// <param name="encoding">文件编码格式</param>
    public void Generate(Stream fileStream, Encoding encoding)
    {
        using StreamWriter writer = new(fileStream, encoding);
        writer.WriteLine(_writer.ToString());
    }


    public void Clear()
    {
        _writer.Clear();
        _contextStack.Clear();
    }
}


public enum FunctionKind
{
    Function,
    Sub
}